/**
 * @(#)SystemMain.java
 *
 * Copyright: Copyright (c) 2003 Carnegie Mellon University
 *
 * This program is the implementation of the original system in the Assignment 1 of "Architectures
 * of Software Systems" class. For the details of each classes, please refer to the provided javadoc
 * documents. You will also find some useful information such as implementation decisions, coding
 * standards, and naming conventions in the README file.
 *
 * History:
 *
 * Date          Author              Change
 * ----------    ----------------    ---------------------------------------------------------------
 * 07/15/2003    Jung Soo Kim        Initial version.
 */


import java.io.*;


/**
 * Dummy class to hold system main method.
 */
public class SystemMain {

    /**
     * Creates components and start the system. Two parameters are expected, the first one as the
     * input file name containing student records and the second one as the output file name to
     * store the records of accepted students.
     *
     * @param args array of input parameters
     */
    public static void main(String[] args) {
        // Check the number of parameters.
        if (args.length != 2) {
            System.out.println("Incorrect number of parameters");
            System.out.println("Usage: java SystemMain <inputfile> <outputfile>");
            System.exit(1);
        }

        // Check if the input file exists.
        if (!new File(args[0]).exists()) {
            System.out.println("Could not find " + args[0]);
            System.exit(1);
        }

        // Check if the parent directory of the output file exists (create a new one if not).
        File parentFile = new File(args[1]).getAbsoluteFile().getParentFile();
        if (!parentFile.exists() && !parentFile.mkdir()) {
            System.out.println("Cound not create the parent directory of " + args[1]);
            System.exit(1);
        }

        try {
            // _____________________________________________________________________
            // Connector and Component Creation and Binding
            // _____________________________________________________________________

            // Create pipes (roles, exactly speaking).
            System.out.println("Controller: Creating connectors ...");
            PipedWriter objTemp;

            BufferedWriter roleMSESource = new BufferedWriter(objTemp = new PipedWriter());
            BufferedReader roleMSESync   = new BufferedReader(new PipedReader(objTemp));

            BufferedWriter roleNonMSESource = new BufferedWriter(objTemp = new PipedWriter());
            BufferedReader roleNonMSESync   = new BufferedReader(new PipedReader(objTemp));

            BufferedWriter roleMSEAcceptedSource = new BufferedWriter(objTemp = new PipedWriter());
            BufferedReader roleMSEAcceptedSync   = new BufferedReader(new PipedReader(objTemp));

            BufferedWriter roleNonMSEAcceptedSource = new BufferedWriter(objTemp = new PipedWriter());
            BufferedReader roleNonMSEAcceptedSync   = new BufferedReader(new PipedReader(objTemp));

            BufferedReader roleInputFileSync    = new BufferedReader(new FileReader(args[0]));
            BufferedWriter roleOutputFileSource = new BufferedWriter(new FileWriter(args[1]));

            // Create filters (pass the roles as parameters to be bound with the ports of each filter).
            System.out.println("Controller: Creating components ...");
            SplitFilter filterSplitMSE
                = new SplitFilter("MSE", roleInputFileSync, roleMSESource, roleNonMSESource, "MSE");
            CourseFilter filterScreen17651
                = new CourseFilter("17651", roleMSESync, roleMSEAcceptedSource, 17651);
			CourseFilter filterScreen21701
                = new CourseFilter("21701", roleNonMSESync, roleNonMSEAcceptedSource, 21701);
            MergeFilter filterMergeAccepted
                = new MergeFilter("Accepted", roleMSEAcceptedSync, roleNonMSEAcceptedSync, roleOutputFileSource);

            // _____________________________________________________________________
            // System Execution
            // _____________________________________________________________________

            // Start all filters.
            System.out.println("Controller: Starting filters ...");
            filterSplitMSE.start();
            filterScreen17651.start();
            filterScreen21701.start();
            filterMergeAccepted.start();

            // Wait until there is no data in the chains of pipe and filters. The order of checking,
            // from input to output, is important to avoid concurrency issues. Think about what will
            // happen if there is a circular pipe-and-filter chain.
            while (roleInputFileSync.ready() || filterSplitMSE.busy()
                    || roleMSESync   .ready() || filterScreen17651.busy() || roleMSEAcceptedSync   .ready()
                    || roleNonMSESync.ready() || filterScreen21701.busy() || roleNonMSEAcceptedSync.ready()
                    || filterMergeAccepted.busy()) {
                // Show some feedback signal and yield the CPU turn.
                System.out.print('.');
                Thread.yield();
            }

            // _____________________________________________________________________
            // System Clean-Up
            // _____________________________________________________________________

            // Dispose of all the filters.
            System.out.println("Controller: disposing of all the components ...");
            filterSplitMSE.interrupt();
            filterScreen17651.interrupt();
            filterScreen21701.interrupt();
            filterMergeAccepted.interrupt();

            // Make sure the filters are dead.
            while (filterSplitMSE.isAlive() == false || filterScreen17651.isAlive() == false
                    || filterScreen21701.isAlive() == false || filterMergeAccepted.isAlive() == false) {
                // Show some feedback signal and yield the CPU turn.
                System.out.print('.');
                Thread.yield();
            }

            // Dispose of all the pipes.
            System.out.println("Controller: disposing of all the connectors ...");
            roleInputFileSync.close();
            roleOutputFileSource.close();
            roleMSESource.close();
            roleMSESync.close();
            roleNonMSESource.close();
            roleNonMSESync.close();
            roleMSEAcceptedSource.close();
            roleMSEAcceptedSync.close();
            roleNonMSEAcceptedSource.close();
            roleNonMSEAcceptedSync.close();
        }
        catch (Exception e) {
            // Dump the exception information for debugging.
            System.out.println("Unexpected exception occurred in the SystemMain.");
            e.printStackTrace();
            System.exit(1);
        }

        // Done!
        System.out.println("Controller: Bye!");
    }
}
